package interpreter;

import java.util.ArrayList;
import java.util.Collections;

import client.Measurement;
import client.ProcessingMeasurement;

public class ComputeDataTask2Parameter {
	public Task2and3Data data;
	public long executedBefore;
	public int i;
	public ArrayList<Measurement> procTime;

	public ComputeDataTask2Parameter(Task2and3Data _data, long executedBefore,
			int i, ArrayList<Measurement> procTimeClient) {
		this.executedBefore = executedBefore;
		this.data = _data;
		this.i = i;
		this.procTime = procTimeClient;
	}

	public void computeIndividualServer(String db_host){
		boolean []initialized_throughput = new boolean [] {false, false, false};
		for(Measurement r : procTime) {
			if(((ProcessingMeasurement)r).host.equals(db_host) == false) {
				continue;
			}
			int id = data.host2id(((ProcessingMeasurement)r).host);
			if(!initialized_throughput[id]) {
				data.server_throughput[id][i] = 0;
				initialized_throughput[id] = true;
			}
			data.server_throughput[id][i] +=1;
		}
		if(initialized_throughput[0]) {
			data.server_throughput[0][i] /= (double)(data.interpreterStep) / 1000.0;
		}
		if(initialized_throughput[1]) {
			data.server_throughput[1][i] /= (double)(data.interpreterStep) / 1000.0;
		}
		if(initialized_throughput[2]) {
			data.server_throughput[2][i] /= (double)(data.interpreterStep) / 1000.0;
		}
		
		int []initialized_mean_service_rate = new int [] {0,0,0};
		for(Measurement r : procTime) {
			if(((ProcessingMeasurement)r).host.equals(db_host) == false) {
				continue;
			}
			int id = data.host2id(((ProcessingMeasurement)r).host);
			if(initialized_mean_service_rate[id] == 0) {
				data.server_mean_service_rate[id][i] = 0;
			}
			data.server_mean_service_rate[id][i] += r.getStop() - r.getStart();
			initialized_mean_service_rate[id] ++;
		}
		if(initialized_mean_service_rate[0] != 0) {
			data.server_mean_service_rate[0][i] /= (double)(initialized_mean_service_rate[0]);
			data.server_utilization[0][i] = ((double)data.interpreterStep - idleTimeForServerId(executedBefore - data.interpreterStep, 0)) / ((double)data.interpreterStep);
		}
		if(initialized_mean_service_rate[1] != 0) {
			data.server_mean_service_rate[1][i] /= (double)(initialized_mean_service_rate[1]);
			data.server_utilization[1][i] = ((double)data.interpreterStep - idleTimeForServerId(executedBefore - data.interpreterStep, 1)) / ((double)data.interpreterStep);
		}
		if(initialized_mean_service_rate[2] != 0) {
			data.server_mean_service_rate[2][i] /= (double)(initialized_mean_service_rate[2]);
			data.server_utilization[2][i] = ((double)data.interpreterStep - idleTimeForServerId(executedBefore - data.interpreterStep, 2)) / ((double)data.interpreterStep);
		}
	}
	
	public void computeDataTask2() {
		data.jA[i] = JobArrival(executedBefore - data.interpreterStep);
		if (i > 0) {
			data.jA[i-1] += LeftOver(executedBefore - data.interpreterStep);
			data.jR[i-1] += data.jA[i-1] - data.jC[i-1];
		}
		data.aSS[i] = AverageTimeSpentInSystem();
		double idle = idleTime(executedBefore - data.interpreterStep);
		data.jC[i] = procTime.size();
		data.busies[i] = data.interpreterStep - idle;
		computeServiceTime();
		computeAverageActiveJobs(executedBefore - data.interpreterStep, executedBefore);
		//computeIndividualServer();
//		computeThinkTime();
	}

//	private void computeThinkTime() {
//		for (Measurement m : procTime) {
//			
//		}
//	}

	private void computeAverageActiveJobs(long beginning, long ending) {
		ArrayList<MeasurementEvent> measurements = new ArrayList<MeasurementEvent>();
		for(Measurement r : procTime) {
			long start = Math.max(r.getStart(), beginning);
			long stop = Math.min(r.getStop(), ending);
			measurements.add(new MeasurementEvent(start, true));
			measurements.add(new MeasurementEvent(stop, false));
		}
		
		Collections.sort(measurements);
		
		int current_active_jobs = 0;
		long last_event_timestamp = beginning;
		double result = 0;
		for(int i=0; i<measurements.size(); i++) {
			if(measurements.get(i).getIsStart()) {
				// Starting
				current_active_jobs ++;
			} else {
				current_active_jobs --;
			}
			
			result += (measurements.get(i).getTimestamp() - last_event_timestamp) * current_active_jobs;
			last_event_timestamp = measurements.get(i).getTimestamp();
		}
		
		data.averageActiveJobs[i] = (result / ((double)(ending-beginning)));
	}

	public double AverageTimeSpentInSystem() {
		double sum = 0;
		for (Measurement r : procTime) {
			sum += r.getStop() - r.getStart();
		}
		return sum / procTime.size();
	}

	public double idleTime(double start) {
		double result = 0;
		if (!procTime.isEmpty()) {
			long start_time = procTime.get(procTime.size() - 1)
					.getStart();
			for (int i = procTime.size() - 2; i >= 0; i--) {
				if (procTime.get(i).getStop() < start_time) {
					// We have a gap
					result += start_time - procTime.get(i).getStop();
					start_time = procTime.get(i).getStart();
				} else {
					start_time = Math.min(start_time, procTime.get(i)
							.getStart());
				}
			}
			if (start_time > start) {
				result += start_time - start;
			}
		} else {
			result = data.interpreterStep;
		}
		return result;
	}

	public double idleTimeForServerId(double start, int sID) {
		double result = 0;
		if (!procTime.isEmpty()) {
			long start_time = 0;
			int i = procTime.size() - 1;
			while(i >= 0) {
				if(data.host2id(((ProcessingMeasurement)procTime.get(i)).host) == sID) {
					start_time = procTime.get(i).getStart();
					break;
				}
				i--;
			}
			if(i==0) {
				return data.interpreterStep;
			}
			
			for (; i >= 0; i--) {
				if(data.host2id(((ProcessingMeasurement)procTime.get(i)).host) != sID) {
					continue;
				}
				if (procTime.get(i).getStop() < start_time) {
					// We have a gap
					result += start_time - procTime.get(i).getStop();
					start_time = procTime.get(i).getStart();
				} else {
					start_time = Math.min(start_time, procTime.get(i)
							.getStart());
				}
			}
			if (start_time > start) {
				result += start_time - start;
			}
		} else {
			result = data.interpreterStep;
		}
		return result;
	}

	
	public long LeftOver(long l) {
		long result = 0;
		for (Measurement rm : procTime) {
			if (rm.getStart() <= l) {
				result = result + 1;
			}
		}
		return result;
	}

	public long JobArrival(long l) {
		long result = 0;
		for (Measurement rm : procTime) {
			if (rm.getStart() > l) {
				result = result + 1;
			}
		}
		return result;
	}
	
	public void computeServiceTime() {
		data.serviceTime[i] = 0.0;
		for (Measurement m : procTime) {
			data.serviceTime[i] += ((double) m.getStop() - m.getStart());
		}
		data.serviceTime[i] /= data.busies [i];
	}
}